/*     */ package com.zimbra.cs.store.triton;
/*     */ 
/*     */ import com.google.common.annotations.VisibleForTesting;
/*     */ import com.zimbra.common.httpclient.HttpClientUtil;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraHttpConnectionManager;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.service.UserServlet.HttpInputStream;
/*     */ import com.zimbra.cs.store.Blob;
/*     */ import com.zimbra.cs.store.StoreManager.StoreFeature;
/*     */ import com.zimbra.cs.store.external.ExternalResumableIncomingBlob;
/*     */ import com.zimbra.cs.store.external.ExternalResumableUpload;
/*     */ import com.zimbra.cs.store.external.ExternalUploadedBlob;
/*     */ import com.zimbra.cs.store.external.SisStore;
/*     */ import java.io.ByteArrayInputStream;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.security.DigestInputStream;
/*     */ import java.security.MessageDigest;
/*     */ import java.security.NoSuchAlgorithmException;
/*     */ import org.apache.commons.codec.binary.Hex;
/*     */ import org.apache.commons.httpclient.HttpClient;
/*     */ import org.apache.commons.httpclient.methods.DeleteMethod;
/*     */ import org.apache.commons.httpclient.methods.GetMethod;
/*     */ import org.apache.commons.httpclient.methods.PostMethod;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class TritonBlobStoreManager
/*     */   extends SisStore
/*     */   implements ExternalResumableUpload
/*     */ {
/*     */   private String url;
/*     */   private String blobApiUrl;
/*     */   private HashType hashType;
/*     */   private String emptyLocator;
/*     */   
/*     */   static enum HashType
/*     */   {
/*  55 */     SHA0,  SHA256;
/*     */     
/*     */     private HashType() {}
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   public TritonBlobStoreManager(String url, HashType hashType) {
/*  62 */     this.url = url;
/*  63 */     this.hashType = hashType;
/*     */   }
/*     */   
/*     */ 
/*     */   public TritonBlobStoreManager() {}
/*     */   
/*     */   public void startup()
/*     */     throws IOException, ServiceException
/*     */   {
/*  72 */     if (this.url == null) {
/*  73 */       this.url = LC.triton_store_url.value();
/*     */     }
/*  75 */     this.blobApiUrl = (this.url + "/blob/");
/*  76 */     if (this.hashType == null) {
/*  77 */       this.hashType = HashType.valueOf(LC.triton_hash_type.value());
/*     */     }
/*  79 */     MessageDigest digest = newDigest();
/*  80 */     this.emptyLocator = getLocator(digest.digest());
/*  81 */     ZimbraLog.store.info("TDS Blob store manager using url %s hashType %s", new Object[] { this.url, this.hashType });
/*  82 */     super.startup();
/*     */   }
/*     */   
/*     */   protected String getLocator(Blob blob) throws ServiceException, IOException
/*     */   {
/*  87 */     if ((blob instanceof TritonBlob))
/*     */     {
/*  89 */       return ((TritonBlob)blob).getLocator();
/*     */     }
/*     */     
/*  92 */     return getLocator(getHash(blob));
/*     */   }
/*     */   
/*     */ 
/*     */   public String getLocator(byte[] hash)
/*     */   {
/*  98 */     return Hex.encodeHexString(hash);
/*     */   }
/*     */   
/*     */   public byte[] getHash(Blob blob) throws ServiceException, IOException
/*     */   {
/* 103 */     MessageDigest digest = newDigest();
/* 104 */     DigestInputStream dis = null;
/* 105 */     InputStream bis = null;
/*     */     try {
/* 107 */       bis = blob.getInputStream();
/* 108 */       dis = new DigestInputStream(bis, digest);
/* 109 */       while (dis.read() >= 0) {}
/*     */       
/* 111 */       return digest.digest();
/*     */     } finally {
/* 113 */       ByteUtil.closeStream(bis);
/* 114 */       ByteUtil.closeStream(dis);
/*     */     }
/*     */   }
/*     */   
/*     */   private MessageDigest newDigest() throws ServiceException {
/*     */     MessageDigest digest;
/* 120 */     switch (this.hashType) {
/*     */     case SHA256: 
/*     */       try {
/* 123 */         digest = MessageDigest.getInstance("SHA-256");
/*     */       } catch (NoSuchAlgorithmException e) {
/* 125 */         throw ServiceException.FAILURE("unable to load SHA256 digest due to exception", e);
/*     */       }
/*     */     
/*     */ 
/*     */ 
/*     */     case SHA0: 
/*     */       try
/*     */       {
/* 133 */         digest = (MessageDigest)Class.forName("cryptix.provider.md.SHA0").newInstance();
/*     */       } catch (Exception e) {
/* 135 */         throw ServiceException.FAILURE("unable to load SHA0 digest due to exception", e);
/*     */       }
/*     */     default: 
/* 138 */       throw ServiceException.FAILURE("Unknown hashType " + this.hashType, null);
/*     */     }
/* 140 */     return digest;
/*     */   }
/*     */   
/*     */   protected void writeStreamToStore(InputStream in, long actualSize, Mailbox mbox, String locator) throws IOException, ServiceException
/*     */   {
/* 145 */     HttpClient client = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
/* 146 */     if (actualSize < 0L) {
/* 147 */       throw ServiceException.FAILURE("Must use resumable upload (i.e. StoreManager.newIncomingBlob()) if size is unknown", null);
/*     */     }
/* 149 */     if (actualSize == 0L) {
/* 150 */       ZimbraLog.store.info("storing empty blob");
/* 151 */       return;
/*     */     }
/* 153 */     PostMethod post = new PostMethod(this.blobApiUrl);
/* 154 */     ZimbraLog.store.info("posting to %s with locator %s", new Object[] { post.getURI(), locator });
/*     */     try {
/* 156 */       HttpClientUtil.addInputStreamToHttpMethod(post, in, actualSize, "application/octet-stream");
/* 157 */       post.addRequestHeader("Content-Length", actualSize + "");
/* 158 */       post.addRequestHeader("X-Objectid", locator);
/* 159 */       post.addRequestHeader("X-Mozy-Hash-Type", this.hashType.toString());
/* 160 */       int statusCode = HttpClientUtil.executeMethod(client, post);
/* 161 */       if (statusCode == 201) {
/*     */         return;
/*     */       }
/* 164 */       ZimbraLog.store.error("failed with code %d response: %s", new Object[] { Integer.valueOf(statusCode), post.getResponseBodyAsString() });
/* 165 */       throw ServiceException.FAILURE("unable to store blob " + statusCode + ":" + post.getStatusText(), null);
/*     */     }
/*     */     finally {
/* 168 */       post.releaseConnection();
/*     */     }
/*     */   }
/*     */   
/*     */   public InputStream readStreamFromStore(String locator, Mailbox mbox)
/*     */     throws IOException
/*     */   {
/* 175 */     HttpClient client = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
/* 176 */     GetMethod get = new GetMethod(this.blobApiUrl + locator);
/* 177 */     get.addRequestHeader("X-Mozy-Hash-Type", this.hashType.toString());
/* 178 */     ZimbraLog.store.info("getting %s", new Object[] { get.getURI() });
/* 179 */     int statusCode = HttpClientUtil.executeMethod(client, get);
/* 180 */     if (statusCode == 200) {
/* 181 */       return new UserServlet.HttpInputStream(get);
/*     */     }
/* 183 */     get.releaseConnection();
/* 184 */     if ((statusCode == 404) && (this.emptyLocator.equals(locator)))
/*     */     {
/* 186 */       ZimbraLog.store.info("returning input stream for empty blob");
/* 187 */       return new ByteArrayInputStream(new byte[0]);
/*     */     }
/* 189 */     throw new IOException("unexpected return code during blob GET: " + get.getStatusText());
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   public boolean deleteFromStore(String locator, Mailbox mbox)
/*     */     throws IOException
/*     */   {
/* 197 */     HttpClient client = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
/* 198 */     DeleteMethod delete = new DeleteMethod(this.blobApiUrl + locator);
/* 199 */     delete.addRequestHeader("X-Mozy-Hash-Type", this.hashType.toString());
/*     */     try {
/* 201 */       ZimbraLog.store.info("deleting %s", new Object[] { delete.getURI() });
/* 202 */       int statusCode = HttpClientUtil.executeMethod(client, delete);
/* 203 */       if (statusCode == 200) {
/* 204 */         return true;
/*     */       }
/* 206 */       throw new IOException("unexpected return code during blob DELETE: " + delete.getStatusText());
/*     */     }
/*     */     finally {
/* 209 */       delete.releaseConnection();
/*     */     }
/*     */   }
/*     */   
/*     */   public ExternalResumableIncomingBlob newIncomingBlob(String id, Object ctxt) throws IOException, ServiceException
/*     */   {
/* 215 */     return new TritonIncomingBlob(id, this.url, getBlobBuilder(), ctxt, newDigest(), this.hashType);
/*     */   }
/*     */   
/*     */   public String finishUpload(ExternalUploadedBlob blob) throws IOException, ServiceException
/*     */   {
/* 220 */     TritonBlob tb = (TritonBlob)blob;
/* 221 */     PostMethod post = new PostMethod(this.url + tb.getUploadId());
/* 222 */     ZimbraLog.store.info("posting to %s with locator %s", new Object[] { post.getURI(), tb.getLocator() });
/* 223 */     HttpClient client = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
/*     */     try {
/* 225 */       post.addRequestHeader("X-Objectid", tb.getLocator());
/* 226 */       post.addRequestHeader("X-Mozy-Hash-Type", this.hashType.toString());
/* 227 */       post.addRequestHeader("X-Mozy-Server-Token", tb.getServerToken().getToken());
/* 228 */       int statusCode = HttpClientUtil.executeMethod(client, post);
/* 229 */       if (statusCode == 201) {
/* 230 */         return tb.getLocator();
/*     */       }
/* 232 */       ZimbraLog.store.error("failed with code %d response: %s", new Object[] { Integer.valueOf(statusCode), post.getResponseBodyAsString() });
/* 233 */       throw ServiceException.FAILURE("unable to store blob " + statusCode + ":" + post.getStatusText(), null);
/*     */     }
/*     */     finally {
/* 236 */       post.releaseConnection();
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private boolean sisCreate(byte[] hash)
/*     */     throws IOException
/*     */   {
/* 248 */     String locator = getLocator(hash);
/* 249 */     HttpClient client = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
/* 250 */     PostMethod post = new PostMethod(this.blobApiUrl + locator);
/* 251 */     ZimbraLog.store.info("SIS create URL: %s", new Object[] { post.getURI() });
/*     */     try {
/* 253 */       post.addRequestHeader("X-Mozy-Hash-Type", this.hashType.toString());
/* 254 */       int statusCode = HttpClientUtil.executeMethod(client, post);
/* 255 */       boolean bool; if (statusCode == 201)
/* 256 */         return true;
/* 257 */       if (statusCode == 404) {
/* 258 */         if (this.emptyLocator.equals(locator))
/*     */         {
/* 260 */           return true;
/*     */         }
/* 262 */         return false;
/*     */       }
/* 264 */       if (statusCode == 400)
/*     */       {
/* 266 */         ZimbraLog.store.warn("failed with code %d response: %s", new Object[] { Integer.valueOf(statusCode), post.getResponseBodyAsString() });
/* 267 */         return false;
/*     */       }
/*     */       
/* 270 */       ZimbraLog.store.error("failed with code %d response: %s", new Object[] { Integer.valueOf(statusCode), post.getResponseBodyAsString() });
/* 271 */       throw new IOException("unable to SIS create " + statusCode + ":" + post.getStatusText(), null);
/*     */     }
/*     */     finally {
/* 274 */       post.releaseConnection();
/*     */     }
/*     */   }
/*     */   
/*     */   public Blob getSisBlob(byte[] hash) throws IOException
/*     */   {
/* 280 */     if (sisCreate(hash))
/*     */     {
/* 282 */       return getLocalBlob(null, getLocator(hash));
/*     */     }
/* 284 */     return null;
/*     */   }
/*     */   
/*     */ 
/*     */   public boolean supports(StoreManager.StoreFeature feature)
/*     */   {
/* 290 */     switch (feature) {
/* 291 */     case SINGLE_INSTANCE_SERVER_CREATE:  return this.hashType == HashType.SHA256; }
/* 292 */     return super.supports(feature);
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/store/triton/TritonBlobStoreManager.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */